package impl;

import java.util.HashSet;
import java.util.Set;

import services.BombeService;
import services.MoteurJeuService;
import services.TerrainService;
import services.VilainService;
import utils.Commande;
import utils.Resultat;
import utils.Sante;
import contracts.BombeContract;
import contracts.ContractException;
import contracts.TerrainContract;

public class MoteurJeuImpl implements MoteurJeuService{
	private int maxPasJeu;
	private int pasJeuCourrant;
	private int xHeros;
	private int yHeros;
	private Sante santeHeros;
	private int forceVitaleHeros;
	private TerrainService terrain;
	private Set<BombeService> listeBombe;
	
	private int xKidnappeur;
	private int yKidnappeur;
	private Sante santeKidnappeur;
	private int forceVitaleKidnappeur;
	
	private Set<VilainService> listeVilain;
	
	public MoteurJeuImpl() {
	}
	
	
	/**
	 * getMaxPasJeu() retourne le nombre maximal de pas
	 * que le joueur peut effectuer
	 */
	@Override
	public int getMaxPasJeu() {
		return maxPasJeu;
	}

	/**
	 * getPasJeuCourrant() retourne le nombre 
	 * pas en cours
	 */
	@Override
	public int getPasJeuCourrant() {
		return pasJeuCourrant;
	}
	
	/**
	 * getHerosX() retourne l'abscisse du
	 * hero
	 */
	@Override
	public int getHerosX(){
		return xHeros;
	}
	
	/**
	 * getHerosY() retourne l'ordonnee du
	 * hero
	 */
	@Override
	public int getHerosY(){
		return yHeros;
	}
	
	/**
	 * getKidnappeurX() retourne l'abscisse du
	 * kidnappeur
	 */
	@Override
	public int getKidnappeurX(){
		return xKidnappeur;
	}
	
	/**
	 * getKidnappeurY() retourne l'ordonnee du
	 * kidnappeur
	 */
	@Override
	public int getKidnappeurY(){
		return yKidnappeur;
	}
	
	/**
	 * getHerosSante() retourne la sante du
	 * hero
	 */
	@Override
	public Sante getHerosSante(){
		return santeHeros;
	}
	
	/**
	 * getKidnappeurSante() retourne la sante du
	 * kidnappeur
	 */
	@Override
	public Sante getKidnappeurSante(){
		return santeKidnappeur;
	}
	
	/**
	 * getHerosForceVitale() retourne la force
	 * vitale du hero
	 */
	@Override
	public int getHerosForceVitale(){
		return forceVitaleHeros;
	}
	
	/**
	 * getHerosForceVitaleKidnappeur() retourne la force
	 * vitale du kidnappeur
	 */
	@Override
	public int getForceVitaleKidnappeur(){
		return forceVitaleKidnappeur;
	}
	
	/**
	 * getTerrain() retourne le terrain de jeu
	 */
	@Override
	public TerrainService getTerrain(){
		return terrain;
	}
	
	/**
	 * getBombeNumeros() retourne la liste des
	 * numeros des bombes en jeu
	 */
	@Override
	public Set<Integer> getBombeNumeros(){
		Set<Integer> res = new HashSet<Integer>();
		for(BombeService s : this.listeBombe){
			res.add(s.getNumero());
		}
		
		return res;
	}
	
	/**
	 * getNbBombes() retourne le nombre de
	 * bombes en jeu
	 */
	@Override
	public int getNbBombes(){
		return this.listeBombe.size();
	}
	
	/**
	 * getNbVilain() retourne le nombre de
	 * vilain en jeu
	 */
	@Override
	public int getNbVilains(){
		return this.listeVilain.size();
	}
	
	/**
	 * getNbVilain() retourne la liste des
	 * vilains en jeu
	 */
	@Override
	public Set<VilainService> getListeVilain(){
		return this.listeVilain;
	}
	
	/**
	 * bombeExiste(num) retourne true si la bombe
	 * de numero num existe, false sinon
	 */
	@Override
	public boolean bombeExiste(int num){
		for(BombeService s : this.listeBombe){
			if(s.getNumero() == num){
				return true;
			}
		}
		return false;
	}
	
	/**
	 * vilainExiste(num) retourne true si le vilain
	 * de numero num existe, false sinon
	 */
	@Override
	public boolean vilainExiste(int num){
		for(VilainService v : this.listeVilain){
			if(v.getNumero() == num){
				return true;
			}
		}
		return false;
	}
	
	/**
	 * getBombe(num) retourne la bombe de numero num
	 */
	@Override
	public BombeService getBombe(int num){
		for(BombeService s : this.listeBombe){
			if(s.getNumero() == num){
				return s;
			}
		}
		return null;
	}
	
	/**
	 * getVilain(num) retourne le vilain de numero num
	 */
	@Override
	public VilainService getVilain(int num) throws ContractException{
		for(VilainService v : this.listeVilain){
			if(v.getNumero() == num){
				return v;
			}
		}
		return null;
	}
	
	/**
	 * estFini() retourne true si la partie est
	 * fini, false sinon
	 */
	@Override
	public boolean estFini(){
		if(getHerosSante() == Sante.MORT || getPasJeuCourrant() == getMaxPasJeu())
			return true;
		return false;
	}
	
	/**
	 * resultatFinal() retourne le resultat de
	 * la partie
	 */
	@Override
	public Resultat resultatFinal(){
		if(santeKidnappeur == Sante.MORT)
			return Resultat.HEROSGAGNE;
		if(santeHeros == Sante.MORT)
			return Resultat.KIDNAPPEURGAGNE;
		return Resultat.PARTIENULLE;
	}
	
	/**
	 * misEnJoue(x, y, num) retourne true si un 
	 * joueur est prit dans l'explosion d'une bombe,
	 * false sinon
	 */
	@Override
	public boolean misEnJoue(int x, int y, int num){
		BombeService b = getBombe(num);
		if((b.getX() == getHerosX() && 
			Math.abs(b.getY() - getHerosY()) <= b.getAmplitude())
			|| (b.getY() == getHerosY() 
			&& Math.abs(b.getX() - getHerosX()) <= b.getAmplitude()))
			return true;
		return false;
	}

	/**
	 * init(int maxPasJeu) initialise l'instance
	 * @throws ContractException 
	 */
	@Override
	public void init(int maxPasJeu) throws ContractException {
		this.maxPasJeu = maxPasJeu;
		this.pasJeuCourrant = 0;
		this.terrain = new TerrainContract(new TerrainImpl());
		this.terrain.init(15, 13);
		this.xHeros = 2;
		this.yHeros = 2;
		this.xKidnappeur = this.terrain.getNombreLignes() - 1;
		this.yKidnappeur = this.terrain.getNombreColonnes() - 1;
		this.santeHeros = Sante.VIVANT;
		this.santeKidnappeur = Sante.VIVANT;
		this.forceVitaleHeros = 3;
		this.forceVitaleKidnappeur = 3;
		this.listeBombe = new HashSet<BombeService>();
		this.listeVilain = new HashSet<VilainService>();
	}
	
	/**
	 * pasJeu() effectue les verifications d'un pas de jeu,
	 * commandes des joueurs, explosion des bombes etc
	 */
	@Override
	public void pasJeu(Commande c, boolean bool){
		for(BombeService s : this.listeBombe){
			s.setCompteARebours(s.getCompteARebours() - 1);
			if(s.vaExploser()){
				if(misEnJoue(getHerosX(), getHerosY(), s.getNumero())){
					this.santeHeros = Sante.MORT;
				} else if(misEnJoue(this.xKidnappeur, this.yKidnappeur, s.getNumero())){
					this.santeKidnappeur = Sante.MORT;
				}
				this.listeBombe.remove(s);
			}
		}
		if(c == Commande.GAUCHE){
			if(bool){
				if(this.getHerosX() > 1){
					this.xHeros--;
				}
			} else {
				if(this.getKidnappeurX() > 1){
					this.xKidnappeur--;
				}
			}
		}
		if(c == Commande.DROITE){
			if(bool){
				if(this.getHerosX() < this.getTerrain().getNombreColonnes()){
					this.xHeros++;
				}
			} else {
				if(this.getKidnappeurX() < this.getTerrain().getNombreColonnes()){
					this.xKidnappeur++;
				}
			}
		}
		if(c == Commande.HAUT){
			if(bool){
				if(this.getHerosY() > 1){
					this.yHeros--;
				}
			} else {
				if(this.getKidnappeurY() > 1){
					this.yKidnappeur--;
				}
			}
		}
		if(c == Commande.BAS){
			if(bool){
				if(this.getHerosY() < this.getTerrain().getNombreLignes()){
					this.yHeros++;
				}
			} else {
				if(this.getKidnappeurY() < this.getTerrain().getNombreLignes()){
					this.yKidnappeur++;
				}
			}
		}
		if(c == Commande.BOMBE){
			int numNextBombe = this.getBombeNumeros().size();
			
			BombeService b = new BombeContract(new BombeImpl());
			try {
				if(bool){
					b.init(numNextBombe, getHerosX(), getHerosY(), getHerosForceVitale());
				} else {
					b.init(numNextBombe, getKidnappeurX(), getKidnappeurY(), getForceVitaleKidnappeur());
				}
			} catch (ContractException e) {
				e.printStackTrace();
				}
			this.listeBombe.add(b);
		}
		this.pasJeuCourrant++;
	}

}
